﻿/*************************************************************************************
 * 文 件 名:   HK_Player
 * 
 * 描    述:   RoamingCam的主要控制脚本
 * 
 * 版    本：  V1.0
 * 创 建 者：  谭潇杰、京产肠饭
 * 创建时间：  2022/1/28
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using DG.Tweening;
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.AI;

public enum ViewMode
{
    Roam, // 第一人称漫游
    BirdView,  // 鸟瞰俯视
    ThreeD   // 透视俯视  
}

public class HK_Player : MonoBehaviour
{
    static HK_Player _instance;
    public static HK_Player Ins
    {
        get { return _instance; }
    }

    ViewMode viewMode;
    public ViewMode ViewMode
    {
        get { return viewMode; }
        set
        {
            ViewModeChangeEvent?.Invoke(value);
            viewMode = value;
        }
    }

    // 切换视角的事件，供外界注册
    public event Action<ViewMode> ViewModeChangeEvent;

    // 是否是移动端
    public bool isMobile;

    // 是否可以操作摄像机
    bool isCanRotCam;
    public bool IsCanRotCam
    {
        get { return isCanRotCam; }
        set
        {
            isCanRotCam = value;
            IsCanRotCamEvent?.Invoke(value);
        }
    }
    public event Action<bool> IsCanRotCamEvent;

    // 上一个位移过来的点位
    public HK_Point lastPoint;

    [Header("摄像机旋转控制器："), SerializeField]
    HK_CameraCtrl cameraCtrl;
    [Header("鸟瞰和三维的摄像机点位："), SerializeField]
    Transform overlookPos_TF;

    // 导航的角色组件
    NavMeshAgent agent;
    // 摄像机的Transform
    Transform mainCam_TF;

    //定义变量存放当前摄像机的透视和正视矩阵信息
    Matrix4x4 fromMatrix, toMatrix;
    //插值计算时应用的存储变量
    Matrix4x4 tempMatrix = new Matrix4x4();

    void Awake()
    {
        _instance = this;

        agent = GetComponent<NavMeshAgent>();
        mainCam_TF = Camera.main.transform;
        ViewMode = ViewMode.Roam;

        IsCanRotCam = true;
    }

    /// <summary>
    /// 导引部分地标使用
    /// </summary>
    /// <param name="targetPoint"></param>
    public void CameraMove2Target(HK_Point targetPoint)
    {
        IsCanRotCam = false;

        mainCam_TF.DOLocalRotate(Vector3.zero, 0.3f).SetEase(Ease.OutSine);

        transform.DOLookAt(new Vector3(targetPoint.transform.position.x, transform.position.y, targetPoint.transform.position.z), 0.3f)
                 .SetEase(Ease.OutSine)
                 .OnComplete(() =>
                 {
                     agent.enabled = true;
                     agent.SetDestination(targetPoint.transform.position);

                     StartCoroutine(CameraMove2Target_IE(targetPoint));
                 });

        lastPoint = targetPoint;
    }
    IEnumerator CameraMove2Target_IE(HK_Point targetPoint)
    {
        while (true)
        {
            yield return null;

            if (agent.remainingDistance <= 0.01f)
            {
                agent.enabled = false;
                SetPlayerLooAtByHK_Point(targetPoint);
                break;
            }
        }
    }

    /// <summary>
    /// 切换到鸟瞰模式
    /// </summary>
    /// <returns></returns>
    public void ChangeToBirdViewMode()
    {
        if (ViewMode == ViewMode.BirdView)
            return;

        IsCanRotCam = false;

        // 将Camera从Player父物体中独立出来
        mainCam_TF.SetParent(null);
        cameraCtrl.enabled = false;

        // 设置PC或移动端时，摄像机正交值的大小
        if (isMobile)
            Camera.main.orthographicSize = 28;
        else
            Camera.main.orthographicSize = 28;

        if (mainCam_TF.position != overlookPos_TF.position)
        {
            mainCam_TF.DOMove(overlookPos_TF.position, 1f)
                      .OnComplete(() => StartCoroutine(ChangeProjection_IE()));
        }
        else
        {
            StartCoroutine(ChangeProjection_IE());
        }

        mainCam_TF.DORotate(overlookPos_TF.localEulerAngles, 1f).SetDelay(0.3f)
                  .OnComplete(() =>
                  {
                      cameraCtrl.enabled = true;
                      IsCanRotCam = true;
                  });

        ViewMode = ViewMode.BirdView;
    }

    /// <summary>
    /// 切换到三维俯视模式
    /// </summary>
    /// <returns></returns>
    public void ChangeToThreeDMode()
    {
        if (ViewMode == ViewMode.ThreeD)
            return;

        IsCanRotCam = false;

        mainCam_TF.SetParent(null);
        cameraCtrl.enabled = false;

        if (ViewMode == ViewMode.BirdView)
        {
            StartCoroutine(ChangeProjection_IE());
            mainCam_TF.DOMove(overlookPos_TF.position, 0.5f)
                      .OnComplete(() =>
                      {
                          cameraCtrl.enabled = true;
                          IsCanRotCam = true;
                      });
        }

        if (ViewMode == ViewMode.Roam)
        {
            mainCam_TF.DOMove(overlookPos_TF.position, 1f);

            mainCam_TF.DORotate(overlookPos_TF.localEulerAngles, 1f).SetDelay(0.3f)
                      .OnComplete(() =>
                      {
                          cameraCtrl.enabled = true;
                          IsCanRotCam = true;
                      });
        }

        ViewMode = ViewMode.ThreeD;
    }

    /// <summary>
    /// 切换到漫游模式 
    /// </summary>
    /// <param name="newPos">新的路径点</param>
    /// <returns></returns>
    public void ChangeToRoamMode(HK_Point targetPoint)
    {
        if (ViewMode == ViewMode.Roam)
            return;

        IsCanRotCam = false;
        cameraCtrl.enabled = false;

        if (ViewMode == ViewMode.ThreeD)
            StartCoroutine(ChangeToRoamMode_IE(false, targetPoint));

        if (ViewMode == ViewMode.BirdView)
            StartCoroutine(ChangeToRoamMode_IE(true, targetPoint));

        lastPoint = targetPoint;

        ViewMode = ViewMode.Roam;
    }

    IEnumerator ChangeToRoamMode_IE(bool isBirdView, HK_Point hK_Point)
    {
        if (isBirdView)  //如果是鸟瞰模式
        {
            StartCoroutine(ChangeProjection_IE());
            yield return new WaitForSeconds(0.35f);
        }

        transform.position = hK_Point.TargetPos;

        mainCam_TF.SetParent(transform);
        mainCam_TF.DOLocalMove(new Vector3(0, cameraCtrl.initCamPosY, 0), 1.2f);

        yield return new WaitForSeconds(1f);

        SetPlayerLooAtByHK_Point(hK_Point);
    }

    // 切换正交、透视摄像机投影方式，时间为0.3s
    IEnumerator ChangeProjection_IE()
    {
        float currentT = 0f;

        while (currentT < 0.2f)  // 0.5s后切换正交或透视状态
        {
            yield return null;

            currentT += Time.deltaTime * 0.4f;

            if (Camera.main.orthographic) //如果当前摄像机为正视状态
            {
                fromMatrix = Camera.main.projectionMatrix;

                Camera.main.orthographic = false;
                Camera.main.ResetProjectionMatrix();

                toMatrix = Camera.main.projectionMatrix;

                Camera.main.orthographic = true;
                Camera.main.projectionMatrix = MatrixLerp(fromMatrix, toMatrix, currentT * currentT);
            }
            else //否则当前摄像机为透视状态
            {
                fromMatrix = Camera.main.projectionMatrix;

                Camera.main.orthographic = true;
                Camera.main.ResetProjectionMatrix();

                toMatrix = Camera.main.projectionMatrix;

                Camera.main.orthographic = false;
                Camera.main.projectionMatrix = MatrixLerp(fromMatrix, toMatrix, currentT);
            }
        }

        Camera.main.orthographic = !Camera.main.orthographic;
        Camera.main.ResetProjectionMatrix();
    }

    Matrix4x4 MatrixLerp(Matrix4x4 from, Matrix4x4 to, float t)
    {
        t = Mathf.Clamp(t, 0.0f, 1.0f);

        tempMatrix.SetRow(0, Vector4.Lerp(from.GetRow(0), to.GetRow(0), t));
        tempMatrix.SetRow(1, Vector4.Lerp(from.GetRow(1), to.GetRow(1), t));
        tempMatrix.SetRow(2, Vector4.Lerp(from.GetRow(2), to.GetRow(2), t));
        tempMatrix.SetRow(3, Vector4.Lerp(from.GetRow(3), to.GetRow(3), t));
        return tempMatrix;
    }

    //通过HK_Point设置Player的朝向
    void SetPlayerLooAtByHK_Point(HK_Point hK_Point)
    {
        switch (hK_Point.lookAtType)
        {
            case LookAtType.Diffuse:

                if (mainCam_TF.localEulerAngles != Vector3.zero)
                {
                    mainCam_TF.DOLocalRotate(Vector3.zero, 0.35f)
                              .OnComplete(() =>
                              {
                                  cameraCtrl.mouseLook.Init(transform, mainCam_TF);
                                  cameraCtrl.enabled = true;
                                  IsCanRotCam = true;
                              });
                }
                else
                {
                    cameraCtrl.mouseLook.Init(transform, mainCam_TF);
                    cameraCtrl.enabled = true;
                    IsCanRotCam = true;
                }
                break;

            case LookAtType.ByQuaternion:
                Vector3 targetEuler = hK_Point.lookAtRot.eulerAngles;

                mainCam_TF.DORotateQuaternion(Quaternion.Euler(new Vector3(targetEuler.x, targetEuler.y, 0)), 1f);

                transform.DORotateQuaternion(Quaternion.Euler(new Vector3(transform.eulerAngles.x, targetEuler.y, transform.eulerAngles.z)), 1f)
                    .OnComplete(() =>
                    {
                        cameraCtrl.mouseLook.Init(transform, mainCam_TF);
                        cameraCtrl.enabled = true;
                        IsCanRotCam = true;
                    });
                break;

            case LookAtType.ByTarget:
                Vector3 newPos = hK_Point.lookAtPos;
                transform.DOLookAt(new Vector3(newPos.x, transform.position.y, newPos.z), 1f);
                cameraCtrl.transform.DOLookAt(newPos, 1f)
                         .OnComplete(() =>
                         {
                             Vector3 camTargetEuler = new Vector3(cameraCtrl.transform.localEulerAngles.x, 0, 0);

                             if (camTargetEuler.x > 180)
                                 camTargetEuler.x -= 360;
                             else if (camTargetEuler.x < -180)
                                 camTargetEuler.x += 360;

                             cameraCtrl.transform.DOLocalRotateQuaternion(Quaternion.Euler(camTargetEuler), 1f)
                             .OnComplete(() =>
                             {
                                 cameraCtrl.mouseLook.Init(transform, mainCam_TF);
                                 cameraCtrl.enabled = true;
                                 IsCanRotCam = true;
                             });
                         });
                break;
        }
    }
}